1 <?php
2 $currDir = dirname(__FILE__);
3 require("{$currDir}/incCommon.php");
4
5 $GLOBALS['page_title'] = $Translation['view members'];
6
7 // get memberID of guest user
8 $anonMemberID = strtolower($adminConfig['anonymousMember']);
9 $anonGroup = $adminConfig['anonymousGroup'];
10
11 /* no editing of guest user */
12 if(strtolower($_REQUEST['memberID']) == $anonMemberID || strtolower($_REQUEST['oldMemberID']) == $anonMemberID){
13 redirect('admin/pageViewMembers.php');
14 exit;
15 }
16
17 include("{$currDir}/incHeader.php");
18
19 $memberID = '';
20 // request to save changes?
21 if(isset($_POST['saveChanges'])){
22 // csrf check
23 if(!csrf_token(true)){
24 echo Notification::show(array(
25 'message' => $Translation['invalid security token'],
26 'class' => 'danger',
27 'dismiss_seconds' => 5000
28 ));
29 include("{$currDir}/incFooter.php");
30 }
31
32 // validate data
33 $oldMemberID = makeSafe(strtolower($_POST['oldMemberID']));
34 $password = makeSafe($_POST['password']);
35 $email = isEmail($_POST['email']);
36 $groupID = intval($_POST['groupID']);
37 $isApproved = ($_POST['isApproved'] == 1 ? 1 : 0);
38 $isBanned = ($_POST['isBanned'] == 1 ? 1 : 0);
39 $customs = array();
40 for($cust = 1; $cust <= 4; $cust++){
41 $customs[$cust] = makeSafe($_POST["custom{$cust}"]);
42 }
43 $comments = makeSafe($_POST['comments']);
44
45 ###############################
46 // new member or old?
47 if(!$oldMemberID){ // new member
48 // make sure member name is unique
49 $memberID = is_allowed_username($_POST['memberID']);
50 if(!$memberID){
51 echo Notification::show(array(
52 'message' => $Translation['username error'],
53 'class' => 'danger',
54 'dismiss_seconds' => 5000
55 ));
56 include("{$currDir}/incFooter.php");
57 }
58
59 // add member
60 $customs_sql = '';
61 foreach($customs as $i => $cust_value){
62 $customs_sql .= "custom{$i}='{$cust_value}', ";
63 }
64 sql("INSERT INTO `membership_users` set memberID='{$memberID}', passMD5='" . md5($password) . "', email='{$email}', signupDate='" . @date('Y-m-d') . "', groupID='{$groupID}', isBanned='{$isBanned}', isApproved='{$isApproved}', {$customs_sql} comments='{$comments}'", $eo);
65
66 if($isApproved){
67 notifyMemberApproval($memberID);
68 }
69
70 // redirect to member editing page
71 redirect("admin/pageEditMember.php?memberID={$memberID}&new_member=1");
72 exit;
73 }else{ // old member
74 // make sure new member username, if applicable, is valid
75 $memberID = makeSafe(strtolower($_POST['memberID']));
76
77 // for super admin user, no username change allowed here
78 $superadmin = (strtolower($adminConfig['adminUsername']) == $oldMemberID);
79 if($superadmin) $memberID = $oldMemberID;
80
81 if($oldMemberID != $memberID)
82 $memberID = is_allowed_username($_POST['memberID']);
83
84 if(!$memberID){
85 echo Notification::show(array(
86 'message' => $Translation['username error'],
87 'class' => 'danger',
88 'dismiss_seconds' => 5000
89 ));
90 include("{$currDir}/incFooter.php");
91 }
92
93 // get current approval state
94 $oldIsApproved = sqlValue("select isApproved from membership_users where lcase(memberID)='{$oldMemberID}'");
95
96 // get member group ID
97 $oldGroupID = sqlValue("select groupID from membership_users where lcase(memberID)='{$oldMemberID}'");
98
99 // update member info
100 $customs_sql = '';
101 $non_superadmin_sql = "passMD5=" . ($password != '' ? "'" . md5($password) . "'" : "passMD5") . ", email='{$email}', groupID='{$groupID}', isBanned='{$isBanned}', isApproved='{$isApproved}', ";
102 foreach($customs as $i => $cust_value){
103 $customs_sql .= "custom{$i}='{$cust_value}', ";
104 }
105
106 if($superadmin){
107 $admin_pass_md5 = makeSafe($adminConfig['adminPassword'], false);
108 $admin_email = makeSafe($adminConfig['senderEmail'], false);
109 $non_superadmin_sql = "passMD5='{$admin_pass_md5}', email='{$admin_email}', isBanned='0', isApproved='1', ";
110 }
111
112 $upQry = "UPDATE `membership_users` set memberID='{$memberID}', {$non_superadmin_sql} {$customs_sql} comments='{$comments}' WHERE lcase(memberID)='{$oldMemberID}'";
113 sql($upQry, $eo);
114
115 // if memberID was changed, update membership_userrecords
116 if($oldMemberID != $memberID){
117 sql("update membership_userrecords set memberID='{$memberID}' where lcase(memberID)='{$oldMemberID}'", $eo);
118 }
119
120 // if groupID was changed, update membership_userrecords
121 if($oldGroupID != $groupID && !$superadmin){
122 sql("update membership_userrecords set groupID='{$groupID}' where lcase(memberID)='{$oldMemberID}'", $eo);
123 }
124
125 // if member was approved, notify him
126 if($isApproved && !$oldIsApproved){
127 notifyMemberApproval($memberID);
128 }
129
130 // redirect to member editing page
131 redirect("admin/pageEditMember.php?saved=1&memberID=" . urlencode($memberID));
132 exit;
133 }
134 }elseif($_GET['memberID'] != ''){
135 // we have an edit request for a member
136 $memberID = makeSafe(strtolower($_GET['memberID']));
137 $superadmin = (strtolower($adminConfig['adminUsername']) == $memberID);
138 }elseif($_GET['groupID'] != ''){
139 // show the form for adding a new member, and pre-select the provided group
140 $groupID = intval($_GET['groupID']);
141 $group_name = sqlValue("select name from membership_groups where groupID='$groupID'");
142 if($group_name)
143 $addend = " to '{$group_name}'";
144 }
145
146 if($memberID != ''){
147 // fetch group data to fill in the form below
148 $res = sql("select * from membership_users where lcase(memberID)='{$memberID}'", $eo);
149 if(!($row = db_fetch_assoc($res))){
150 // no such member exists
151 echo Notification::show(array(
152 'message' => $Translation['member not found'],
153 'class' => 'danger',
154 'dismiss_seconds' => 5000
155 ));
156 include("{$currDir}/incFooter.php");
157 }
158
159 // get member data
160 $email = $row['email'];
161 $groupID = $row['groupID'];
162 $isApproved = $row['isApproved'];
163 $isBanned = $row['isBanned'];
164 $customs = array();
165 for($cust = 1; $cust <= 4; $cust++){
166 $customs[$cust] = html_attr($row["custom{$cust}"]);
167 }
168 $comments = html_attr($row['comments']);
169
170 //display dismissible alert for new members and successful saves
171 if(isset($_GET['new_member'])){
172 echo Notification::show(array(
173 'message' => str_replace('<USERNAME>', "<b><i>{$memberID}</i></b>", $Translation['member added']),
174 'class' => 'success',
175 'dismiss_seconds' => 20
176 ));
177 }elseif(isset($_GET['saved'])){
178 echo Notification::show(array(
179 'message' => str_replace('<USERNAME>', "<b><i>{$memberID}</i></b>", $Translation['member updated']),
180 'class' => 'success',
181 'dismiss_seconds' => 20
182 ));
183 }
184 }
185
186 $userPermissionsNote = '';
187 if($memberID != '' && $groupID != sqlValue("select groupID from membership_groups where name='Admins'")){
188 $userPermissionsNote = '<span class="help-block">' . str_replace('<GROUPID>', $groupID, $Translation["user has group permissions"]) . '</span>';
189
190 if(sqlValue("select count(1) from membership_userpermissions where memberID='$memberID'") > 0){
191 $userPermissionsNote = '<span class="help-block">' . $Translation["user has special permissions"] . '</span>';
192 }
193
194 $userPermissionsNote .= '<button type="button" class="btn btn-danger" id="special-permissions">' . html_attr($Translation['set user special permissions']) . '</button>';
195 }
196 ?>
197
198 <div class="page-header">
199 <h1>
200 <?php echo ($memberID ? str_replace('<MEMBERID>', '<span class="text-primary">' . $memberID . '</span>', $Translation["edit member"]) : $Translation["add new member"] . $addend); ?>
201 <div class="pull-right">
202 <div class="btn-group">
203 <a href="pageViewMembers.php" class="btn btn-default btn-lg"><i class="glyphicon glyphicon-arrow-left"></i> <span class="hidden-xs hidden-sm"><?php echo $Translation['back to members']; ?></span></a>
204 <?php if($memberID){ ?>
205 <a href="pageViewRecords.php?memberID=<?php echo urlencode($memberID); ?>" class="btn btn-default btn-lg"><i class="glyphicon glyphicon-th"></i> <span class="hidden-xs hidden-sm"><?php echo $Translation['View member records']; ?></span></a>
206 <a href="pageMail.php?memberID=<?php echo urlencode($memberID); ?>" class="btn btn-default btn-lg"><i class="glyphicon glyphicon-envelope"></i> <span class="hidden-xs hidden-sm"><?php echo $Translation['send message to member']; ?></span></a>
207 <?php } ?>
208 </div>
209 </div>
210 <div class="clearfix"></div>
211 </h1>
212 </div>
213
214
215 <div style="height: 3em;"></div>
216
217 <?php if($superadmin){ ?>
218 <div class="alert alert-warning"><?php echo $Translation["admin member"]; ?></div>
219 <?php } ?>
220
221 <form method="post" action="pageEditMember.php" class="form-horizontal">
222 <?php echo csrf_token(); ?>
223 <input type="hidden" name="oldMemberID" value="<?php echo ($memberID ? html_attr($memberID) : ""); ?>">
224
225 <?php if(!$superadmin){ /* non-admin user fields */ ?>
226 <div class="form-group ">
227 <label for="memberID" class="col-sm-4 col-md-3 col-lg-2 col-lg-offset-2 control-label"><?php echo $Translation["member username"]; ?></label>
228 <div class="col-sm-8 col-md-9 col-lg-6">
229 <input type="text" class="form-control" name="memberID" id="memberID" value="<?php echo html_attr($memberID); ?>" autofocus>
230 <span id="username-available" class="help-block hidden"><i class="glyphicon glyphicon-ok"></i> <?php echo str_ireplace(array("'", '"', '<memberid>'), '', $Translation['user available']); ?></span>
231 <span id="username-not-available" class="help-block hidden"><i class="glyphicon glyphicon-remove"></i> <?php echo str_ireplace(array("'", '"', '<memberid>'), '', $Translation['username invalid']); ?></span>
232 </div>
233 </div>
234
235 <div class="form-group">
236 <label for="password" class="col-sm-4 col-md-3 col-lg-2 col-lg-offset-2 control-label"><?php echo $Translation["password"]; ?></label>
237 <div class="col-sm-8 col-md-9 col-lg-6">
238 <input class="form-control" type="password" name="password" id="password" value="" autocomplete="off">
239 <?php echo ($memberID ? "<span class='help-block'>" . $Translation["change password"] : "" . "</span>"); ?>
240 </div>
241 </div>
242
243 <div class="form-group">
244 <label for="confirmPassword" class="col-sm-4 col-md-3 col-lg-2 col-lg-offset-2 control-label"><?php echo $Translation["confirm password"]; ?> </label>
245 <div class="col-sm-8 col-md-9 col-lg-6">
246 <input class="form-control" type="password" name="confirmPassword" id="confirmPassword" value="" autocomplete="off">
247 </div>
248 </div>
249
250 <div class="form-group">
251 <label for="email" class="col-sm-4 col-md-3 col-lg-2 col-lg-offset-2 control-label"><?php echo $Translation["email"]; ?> </label>
252 <div class="col-sm-8 col-md-9 col-lg-6">
253 <input class="form-control" type="text" id="email" name="email" value="<?php echo $email; ?>">
254 </div>
255 </div>
256
257 <div class="form-group">
258 <label for="group" class="col-sm-4 col-md-3 col-lg-2 col-lg-offset-2 control-label"><?php echo $Translation["group"]; ?></label>
259 <div class="col-sm-8 col-md-9 col-lg-6">
260 <?php
261 $safe_anonGroup = makeSafe($anonGroup, false);
262 echo bootstrapSQLSelect('groupID', "select groupID, name from membership_groups where name!='{$safe_anonGroup}' order by name", $groupID);
263 echo $userPermissionsNote;
264 ?>
265 </div>
266 </div>
267
268 <div class="form-group">
269 <label class="col-sm-4 col-md-3 col-lg-2 col-lg-offset-2 control-label"></label>
270 <div class="col-sm-8 col-md-9 col-lg-6">
271 <div class="checkbox">
272 <label>
273 <input type="checkbox" name="isApproved" value="1" <?php echo ($isApproved ? "checked" : ($memberID ? "" : "checked")); ?>>
274 <?php echo $Translation["approved"]; ?>
275 </label>
276 </div>
277 </div>
278 </div>
279
280 <div class="form-group">
281 <label class="col-sm-4 col-md-3 col-lg-2 col-lg-offset-2 control-label"></label>
282 <div class="col-sm-8 col-md-9 col-lg-6">
283 <div class="checkbox">
284 <label>
285 <input type="checkbox" name="isBanned" value="1" <?php echo ($isBanned ? 'checked' : ''); ?>>
286 <?php echo $Translation['banned']; ?>
287 </label>
288 </div>
289 </div>
290 </div>
291 <?php } /* end of non-admin user fields */ ?>
292
293 <?php for($cust = 1; $cust <= 4; $cust++){ ?>
294 <?php if($adminConfig["custom{$cust}"] != ''){ ?>
295 <div class="form-group">
296 <label for="custom<?php echo $cust; ?>" class="col-sm-4 col-md-3 col-lg-2 col-lg-offset-2 control-label"><?php echo $adminConfig["custom{$cust}"]; ?></label>
297 <div class="col-sm-8 col-md-9 col-lg-6">
298 <input class="form-control" type="text" name="custom<?php echo $cust; ?>" id="custom<?php echo $cust; ?>" value="<?php echo $customs[$cust]; ?>" >
299 </div>
300 </div>
301 <?php } ?>
302 <?php } ?>
303
304 <div class="form-group">
305 <label for="comments" class="col-sm-4 col-md-3 col-lg-2 col-lg-offset-2 control-label"><?php echo $Translation["comments"]; ?> </label>
306 <div class="col-sm-8 col-md-9 col-lg-6">
307 <textarea id="comments" name="comments" rows="10" class="form-control"><?php echo $comments; ?></textarea>
308 </div>
309 </div>
310
311 <div class="form-group">
312 <label class="col-sm-4 col-md-3 col-lg-2 col-lg-offset-2 control-label"></label>
313 <div class="col-sm-8 col-md-9 col-lg-6">
314 <button type="button" id="saveChanges" class="btn btn-primary btn-lg"><i class="glyphicon glyphicon-ok"></i> <?php echo $Translation["save changes"]; ?></button>
315 <?php if($memberID != ''){ /* for existing members, cancel reloads the member */ ?>
316 <a href="pageEditMember.php?memberID=<?php echo urlencode($memberID); ?>" class="btn btn-warning btn-lg hspacer-md"><i class="glyphicon glyphicon-remove"></i> <?php echo $Translation['cancel']; ?></a>
317 <a href="pageViewMembers.php" class="btn btn-default btn-lg hspacer-md"><i class="glyphicon glyphicon-arrow-left"></i> <?php echo $Translation['back to members']; ?></a>
318 <?php }else{ /* for new members, cancel goes to list of members */ ?>
319 <a href="pageViewMembers.php" class="btn btn-warning btn-lg hspacer-md"><i class="glyphicon glyphicon-remove"></i> <?php echo $Translation['cancel']; ?></a>
320 <?php } ?>
321 </div>
322 </div>
323 </form>
324
325 <style>
326 #username-available, #username-not-available{ cursor: pointer; }
327 </style>
328
329
330 <script>
331 $j(function(){
332 var new_member = !$j('[name=oldMemberID]').val().length;
333
334 var uaro; // user availability request object
335 var check_user = function(){
336 // abort previous request, if any
337 if(uaro != undefined) uaro.abort();
338
339 if(!$j('#memberID').length) return true; // username field hidden
340
341 var currentUser = $j('[name=oldMemberID]').val();
342 var memberID = $j('#memberID').val();
343
344 /* no username change, so no need to check it */
345 if(currentUser.length && currentUser == memberID){
346 $j('#username-available, #username-not-available')
347 .addClass('hidden')
348 .parents('.form-group').removeClass('has-error has-success');
349 return;
350 }
351
352 /* username is empty so highlight the error and return without further checks */
353 if(!memberID.length){
354 $j('#username-not-available')
355 .removeClass('hidden')
356 .parents('.form-group').addClass('has-error');
357 return;
358 }
359
360 uaro = $j.ajax(
361 '../checkMemberID.php', {
362 type: 'GET',
363 data: {
364 memberID: memberID,
365 currentUser: currentUser
366 },
367 beforeSend: function(){
368 $j('#username-available, #username-not-available')
369 .addClass('hidden')
370 .parents('.form-group').removeClass('has-error has-success');
371 },
372 success: function(resp){
373 if(resp.match(/\<!-- AVAILABLE --\>/)){
374 $j('#username-available')
375 .removeClass('hidden')
376 .parents('.form-group').addClass('has-success');
377 }else{
378 $j('#username-not-available')
379 .removeClass('hidden')
380 .parents('.form-group').addClass('has-error');
381 }
382 }
383 }
384 );
385 }
386
387 var validate_password = function(){
388 if(!$j('#password').length) return true; // password field hidden
389
390 /* reset error highlights */
391 $j('#password, #confirmPassword').parents('.form-group').removeClass('has-error');
392 $j('#password-mismatch-alert').remove();
393
394 var p1 = $j('#password').val();
395 var p2 = $j('#confirmPassword').val();
396
397 if((p1 != '' && p1 != p2) || (p1 == '' && new_member)){
398 show_notification({
399 message: '<?php echo html_attr($Translation['password mismatch']); ?>',
400 'class': 'danger',
401 dismiss_seconds: 10,
402 id: 'password-mismatch-alert'
403 });
404 $j('#password, #confirmPassword').parents('.form-group').addClass('has-error');
405 $j('#password').focus();
406 return false;
407 }
408
409 return true;
410 }
411
412 var validate_email = function(){
413 if(!$j('#email').length) return true; // email field hidden
414
415 /* reset error highlights */
416 $j('#email').parents('.form-group').removeClass('has-error');
417 $j('#invalid-email-alert').remove();
418
419 /* source: https://stackoverflow.com/a/46181/1945185 */
420 var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
421 if(!re.test($j('#email').val())){
422 show_notification({
423 message: '<?php echo html_attr($Translation['email invalid']); ?>',
424 'class': 'danger',
425 dismiss_seconds: 20,
426 id: 'invalid-email-alert'
427 });
428 $j('#email').focus().parents('.form-group').addClass('has-error');
429
430 return false;
431 }
432
433 return true;
434 }
435
436 var validate_group = function(){
437 if(!$j('#groupID').length) return true; // group field hidden
438
439 /* reset error highlights */
440 $j('#groupID').parents('.form-group').removeClass('has-error');
441 $j('#invalid-group-alert').remove();
442
443 if(!$j('#groupID').val()){
444 show_notification({
445 message: '<?php echo html_attr($Translation['group invalid']); ?>',
446 'class': 'danger',
447 dismiss_seconds: 20,
448 id: 'invalid-group-alert'
449 });
450 $j('#groupID').focus().parents('.form-group').addClass('has-error');
451
452 return false;
453 }
454
455 return true;
456 }
457
458 /* circumvent browser auto-filling of passwords */
459 setTimeout(function(){ $j('#password').val(''); }, 500);
460
461 $j('#username-available, #username-not-available').click(function(){ $j('#memberID').focus(); });
462
463 $j('#memberID').on('keyup blur', check_user);
464
465 /* disable submit button during ajax requests */
466 $j(document)
467 .ajaxStart(function(){
468 $j('#saveChanges').prop('disabled', true);
469 }).ajaxStop(function(){
470 $j('#saveChanges').prop('disabled', false);
471 });
472
473 /* validate form before submitting */
474 $j('#saveChanges').click(function(){
475 /* don't submit form if any ajax requests are still active */
476 if($j.active) return false;
477
478 $j('#general-error-alert').remove();
479
480 if(!validate_password()) return false;
481 if(!validate_email()) return false;
482 if(!validate_group()) return false;
483 check_user();
484
485 if($j('.form-group.has-error').length){
486 /* show general error if no other error alerts displayed */
487 if(!$j('.notifcation-placeholder .alert:not(.invisible)').length){
488 show_notification({
489 message: '<?php echo html_attr($Translation['fix errors before submitting']); ?>',
490 'class': 'danger',
491 dismiss_seconds: 20,
492 id: 'general-error-alert'
493 });
494 $j('.has-error').children('input').focus();
495 }
496 return false;
497 }
498
499 $j('form').append('<input type="hidden" name="saveChanges" value="1">').submit();
500 return true;
501 });
502
503 /* special permissions button */
504 $j('#special-permissions').click(function(){
505 if(confirm('<?php echo html_attr($Translation['sure continue']); ?>')){
506 window.location = 'pageEditMemberPermissions.php?memberID=' + encodeURIComponent($j('[name=oldMemberID]').val());
507 }
508 });
509 })
510 </script>
511
512 <?php
513 include("{$currDir}/incFooter.php");
514 ?>